/*
 * smithWaterman.cpp
 *
 *  Created on: 17-10-2011
 *      Author: pawel
 */


#include"smithWaterman.h"
#include"../enumTypes.h"
#include"../scoreMatrix.h"
#include"../smart2DArray.h"
#include<Bpp/Seq/Sequence.h>
#include"../algorithms.h"
#include<sys/param.h>
#include<stdio.h>



std::list<cell> smithWaterman::run(const optAlgParam & param)
{
	//TODO this is needlemanWunsch modify it
	std::list<cell> alList;
	cell alCell;
	alCell.maxscore = INT_MIN;
	int **scoreMatrix = NULL;
	int **E = NULL;
	int **F = NULL;
//	int EFinit = INT_MIN + param.gopen +param.gext;
	alCell.maxRow = param.dirMatrix.rows -1;
	alCell.maxCol = param.dirMatrix.cols -1;
	try{
		smart2DArray<int> smartScoreMAtrix(param.dirMatrix.rows,param.dirMatrix.cols);
		smart2DArray<int> smartE(param.dirMatrix.rows,param.dirMatrix.cols);
		smart2DArray<int> smartF(param.dirMatrix.rows,param.dirMatrix.cols);
		scoreMatrix = smartScoreMAtrix.getPtr();
		E = smartE.getPtr();
		F = smartF.getPtr();
		/*
		 * Matrix initialization
		 */
		scoreMatrix[0][0] = 0;
		for(size_t i=0;i<param.dirMatrix.rows;i++){
			memset(scoreMatrix[i],0,param.dirMatrix.cols * sizeof(int));
			memset(E[i],0,param.dirMatrix.cols * sizeof(int));
			memset(F[i],0,param.dirMatrix.cols * sizeof(int));
		//	E[i][0] = EFinit;
			//F[i][0] = EFinit;

		}
//		for(size_t i =1;i<param.dirMatrix.cols;i++){
//			//E[0][i] = EFinit;
//			//F[0][i] = EFinit;
//		}

		smart2DArray<int> smartQueryCString(1,param.dirMatrix.rows);
		int *queryCString =(smartQueryCString.getPtr())[0];
		smart2DArray<int> smartDbCString(1,param.dirMatrix.cols);
		int *dbCString = (smartDbCString.getPtr())[0];

		memcpy((void*)queryCString,&(param.querySeqPtr->getContent()[0]),(param.querySeqPtr->getContent()).size() * sizeof(int));
		memcpy((void*)dbCString,&(param.dbSeqPtr->getContent()[0]),(param.dbSeqPtr->getContent()).size() * sizeof(int));
	//queryStr = rows
		int diag,up,left,mScore;

		char **matrix;
		matrix = param.scoreMatrixPtr->getMatrix();
		size_t lineStart;
		size_t lineEnd;
		size_t lastLineEnd;
		bool isRowAboveFilled = false;
		//printf("MIN: %i MAX: %i\n",param.matrixBounds.minDiag,param.matrixBounds.maxDiag);
		for(size_t i =1;i<param.dirMatrix.rows;i++){

			lineStart =MIN(param.dirMatrix.cols -1, MAX(1,(int)(i + param.matrixBounds.minDiag)));
			lineEnd = MAX((long long)lineStart,MIN((long long)(param.dirMatrix.cols -1),(long long)(i+param.matrixBounds.maxDiag)));
			lastLineEnd =MAX((long long)lineStart,MIN((long long)(param.dirMatrix.cols -1),(long long)(i-1+param.matrixBounds.maxDiag)));
			if(lastLineEnd == (size_t)(param.dirMatrix.cols -1)){
				isRowAboveFilled =true;
			}

			if(lineStart == lineEnd)continue;
			//printf("S: %u E: %u (%u) MI: %i MA: %i \n",lineStart,lineEnd,i,param.matrixBounds.minDiag,param.matrixBounds.maxDiag);
			for(size_t j=lineStart;j<=lineEnd;j++){

				//fprintf(stderr,"%i\n",j);
				if(j != lineStart || j == 1){
					E[i][j] = MAX(E[i][j-1] - param.gext, scoreMatrix[i][j-1] -param.gopen);
				}
				else{
					E[i][j] = 0;
				}
				if(isRowAboveFilled){
				F[i][j] = MAX(F[i -1][j] - param.gext, scoreMatrix[i - 1][j] -param.gopen);
				}
				else{
					F[i][j] = 0;
				}


				mScore = matrix[queryCString[i-1]][dbCString[j-1]];
				diag 	= scoreMatrix[i-1][j-1] + mScore;
				up		= F[i][j];
				left	= E[i][j];

				scoreMatrix[i][j] = MAX(0,MAX(diag,MAX(up,left)));

				if(queryCString[i-1] == dbCString[j-1] ){
					param.dirMatrix.matrix[i][j]|= DR_IDENTITY;
				}
				else if(mScore > 0){
					param.dirMatrix.matrix[i][j]|= DR_MATCH;
				}
				else{
					param.dirMatrix.matrix[i][j]|= DR_MISMATCH;
				}
				if( scoreMatrix[i][j]>=alCell.maxscore){
					alCell.maxRow = i;
					alCell.maxCol = j;
					alCell.maxscore = scoreMatrix[i][j];
				}
				if(scoreMatrix[i][j] == 0 ){
					param.dirMatrix.matrix[i][j]|= DR_STOP;
				}
				if(scoreMatrix[i][j] == diag){
					param.dirMatrix.matrix[i][j]|= DR_DIAG;
				}
				if(scoreMatrix[i][j] == up && (isRowAboveFilled)){
					param.dirMatrix.matrix[i][j]|= DR_UP;
				}
				if(scoreMatrix[i][j] == left &&(j == lineStart || j != 1)){
					param.dirMatrix.matrix[i][j]|= DR_LEFT;
				}
				//assert(param.dirMatrix.matrix[i][j] !=0);

			}
		}

		alList.push_front(alCell);
//		printf("NEXT\n");
//		for(size_t i=0;i<param.dirMatrix.rows;i++){
//
//			for(size_t j=0;j<param.dirMatrix.cols;j++){
//				printf("\t %i",scoreMatrix[i][j]);
//			}
//			printf("\n");
//		}

	}catch(std::exception& e){
		exit(1);
		throw;
		}

return alList;

}

smithWaterman::~smithWaterman()
{
}



